package org.my.game.gui;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.*;
import java.util.ArrayList;

public class GamePanel extends JPanel implements Runnable, KeyListener{
	
	public static int WIDTH = 852;//400;
	public static int HEIGHT = 480;//400;
	
	private Thread thread;
	private boolean running;
	
	private BufferedImage image;
	private Graphics2D g;
	
	private int FPS = 60;
	private double averageFPS;
	
	public static Player player;
	public static ArrayList<Bullet> bullets;
	public static ArrayList<Enemy> enemies;
	
	private long waveStartTimer;
	private long waveStartTimerDiff;
	private int waveNumber;
	private boolean waveStart;
	private int waveDelay = 2000;
	
	public GamePanel() {
		super();
		setPreferredSize(new Dimension(WIDTH, HEIGHT));
		setFocusable(true);
		requestFocus();
	}
	
	public void addNotify() {
		super.addNotify();
		if(thread == null) {
			thread = new Thread(this);
			thread.start();
		}
		addKeyListener(this);
	}

	@Override
	public void run() {
		running = true;
		
		image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
		g = (Graphics2D) image.getGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		
		player = new Player();
		bullets = new ArrayList<Bullet>();
		enemies = new ArrayList<Enemy>();
		waveStartTimer = 0;
		waveStartTimerDiff = 0;
		waveStart = true;
		waveNumber = 0;
		
		long startTime, URDTimeMillis, waitTime, totalTime = 0, targetTime = 1000 / FPS;
		int frameCount = 0, maxFrameCount = 30;
		
		while(running) {
			
			startTime = System.nanoTime();
			
			gameUpdate();
			gameRender();
			gameDraw();
			
			URDTimeMillis = (System.nanoTime() - startTime) / 1000000;
			waitTime = targetTime - URDTimeMillis;
			
			try {
				if(waitTime > 0)Thread.sleep(waitTime);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (Exception e2) {
				e2.printStackTrace();
			}
			totalTime += System.nanoTime() - startTime;
			frameCount++;
			if(frameCount == maxFrameCount) {
				averageFPS = 1000.0 / ((totalTime / frameCount) / 1000000);
				frameCount = 0;
				totalTime = 0;
			}
		}
		
	}
	
	private void gameUpdate() {
		
		//new wave
		if(waveStartTimer == 0 && enemies.size() == 0) {
			waveNumber++;
			waveStart = false;
			waveStartTimer = System.nanoTime();
		} else {
			waveStartTimerDiff = (System.nanoTime() - waveStartTimer) / 1000000;
			if(waveStartTimerDiff > waveDelay) {
				waveStart = true;
				waveStartTimer = 0;
				waveStartTimerDiff = 0;
			}
		}
		
		//create enemies
		if(waveStart && enemies.size() == 0) {
			createNewEnemies();
		}
		
		//player Update
		player.update();
		
		//Bullet Update
		try {
			for(Bullet b : bullets) {
				boolean remove = b.update();
				if(remove) {
					bullets.remove(b);
				}
			}
		} catch(Exception e) {
			
		}
		
		//Enemy Update
		try{
			for(Enemy e : enemies) {
				e.update();
				if(e.isDead()) {
					player.addScore(e.getType() + e.getRank());
					enemies.remove(e);
				}
			}
		} catch (Exception e2) {
			
		}
		
		//Bullet-Enemy Collision
		try{
			for(Bullet b : bullets) {
				double bx = b.getX();
				double by = b.getY();
				double br = b.getR();
				
				for(Enemy e : enemies) {
					double ex = e.getX();
					double ey = e.getY();
					double er = e.getR();
					
					double dx = bx -ex;
					double dy = by -ey;
					double dist = Math.sqrt(dx * dx + dy * dy);
					if(dist < br + er) {
						e.hit();
						bullets.remove(b);
						break;
					}
				}
			}
		} catch (Exception e3) {
			
		}
		
		
		//player-enemy collision
		if(!player.isRecovering()) {
			int px =player.getx();
			int py = player.gety();
			int pr = player.getr();
			try{
				for(Enemy e : enemies) {
					double ex = e.getX();
					double ey = e.getY();
					double er = e.getR();
					
					double dx = px - ex;
					double dy = py - ey;
					double dist = Math.sqrt(dx*dx + dy*dy);
					
					if(dist < pr + er) {
						player.loseLife();
					}
					
				}
			}catch(Exception e) {
				
			}
		}
	}
	
	private void gameRender() {
		
		//draw background
		g.setColor(new Color(0, 100, 255));
		g.fillRect(0, 0, WIDTH, HEIGHT);
		
		//Draw Player
		player.draw(g);
		
		//Draw Bullet 
		for(Bullet b : bullets) {
			b.draw(g);
		}
		
		//Draw Enemy 
		for(Enemy e : enemies) {
			e.draw(g);
		}
		
		//draw wave number
		if(waveStartTimer != 0) {
			g.setFont(new Font("Century Gothic", Font.PLAIN, 18));
			String s = "- W A V E   " + waveNumber + "   -";
			int length = (int) g.getFontMetrics().getStringBounds(s, g).getWidth();
			int alpha = (int) (255 * Math.sin(3.14 * waveStartTimerDiff / waveDelay));
			if(alpha > 255) alpha = 255;
			g.setColor(new Color(255, 255, 255, alpha));
			g.drawString(s, WIDTH / 2 - length / 2, HEIGHT / 2);
		}
		
		//draw players lives
		for(int i=0; i < player.getLives(); i++) {
			g.setColor(Color.WHITE);
			g.fillOval(player.getr() * 2 + (player.getr() * i * 3), player.getr() * 2, player.getr() * 2, player.getr() * 2);
			g.setStroke(new BasicStroke(3));
			g.setColor(Color.WHITE.darker());
			g.drawOval(player.getr() * 2 + (player.getr() * i * 3), player.getr() * 2, player.getr() * 2, player.getr() * 2);
			g.setStroke(new BasicStroke(1));
		}
		
		//draw player score
		g.setColor(Color.WHITE);
		g.setFont(new Font("Century Gothic", Font.PLAIN, 18));
		g.drawString("Score: " + player.getScore(), WIDTH - 100, 30);
	}
	
	private void gameDraw() {
		Graphics g2 = this.getGraphics();
		g2.drawImage(image, 0, 0, null);
		g2.dispose();
	}

	private void createNewEnemies() {
		enemies.clear();
		Enemy e;
		
		if(waveNumber == 1) {
			for(int i=0; i<8; i++) {
				enemies.add(new Enemy(1, 1));
			}
		}
		if(waveNumber == 2) {
			for(int i=0; i<8; i++) {
				enemies.add(new Enemy(1, 1));
			}
		}
	}
	
	@Override
	public void keyPressed(KeyEvent e) {
		
		int keyCode = e.getKeyCode();
		if(keyCode == KeyEvent.VK_LEFT) {
			player.setLeft(true);
		}
		if(keyCode == KeyEvent.VK_RIGHT) {
			player.setRight(true);
		}
		if(keyCode == KeyEvent.VK_UP) {
			player.setUp(true);
		}
		if(keyCode == KeyEvent.VK_DOWN) {
			player.setDown(true);
		}
		if(keyCode == KeyEvent.VK_Z) {
			player.setFiring(true);
		}
		
	}

	@Override
	public void keyReleased(KeyEvent e) {

		int keyCode = e.getKeyCode();
		if(keyCode == KeyEvent.VK_LEFT) {
			player.setLeft(false);
		}
		if(keyCode == KeyEvent.VK_RIGHT) {
			player.setRight(false);
		}
		if(keyCode == KeyEvent.VK_UP) {
			player.setUp(false);
		}
		if(keyCode == KeyEvent.VK_DOWN) {
			player.setDown(false);
		}
		if(keyCode == KeyEvent.VK_Z) {
			player.setFiring(false);
		}
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}
	

}
